/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import { cloneDeep, findIndex } from 'lodash';

// Utils
import { COLUMNS, FILTER, ROWS } from '../../../../../utils/constants';

function findAxisForHierarchy(axesStates, hierarchy) {
  const axesName = [ROWS, COLUMNS, FILTER];

  for (const axisName of axesName) {
    const hierarchyIndex = findIndex(axesStates[axisName].items, {
      name: hierarchy
    });

    if (hierarchyIndex !== -1) {
      return { droppableId: axisName, index: hierarchyIndex };
    }
  }

  return null;
}

export const dragDisableLevel = (
  dimensionsDataState,
  levelData,
  draggableSource,
  isDragDisabled = true
) => {
  const dimensionsDataStateClone = cloneDeep(dimensionsDataState);

  return dimensionsDataStateClone.map(dimension => {
    if (dimension.name === levelData.dimension) {
      const hierarchyIndex = findIndex(dimension.hierarchies, {
        uniqueName: levelData.hierarchyUniqueName
      });
      const levelIndex = draggableSource.index;

      if (hierarchyIndex !== -1) {
        dimension.hierarchies[hierarchyIndex].levels[levelIndex][
          'isDragDisabled'
        ] = isDragDisabled;

        return { ...dimension };
      } else {
        return dimension;
      }
    }

    return dimension;
  });
};

export const dragEnableLevel = (
  dimensionsDataState,
  levelData,
  levelName,
  isDragDisabled = false
) => {
  const dimensionsDataStateClone = cloneDeep(dimensionsDataState);

  return dimensionsDataStateClone.map(dimension => {
    if (dimension.name === levelData.dimension) {
      const hierarchyIndex = findIndex(dimension.hierarchies, {
        uniqueName: levelData.name
      });

      if (hierarchyIndex !== -1) {
        const levelIndex = findIndex(
          dimension.hierarchies[hierarchyIndex].levels,
          {
            hierarchyUniqueName: levelData.name,
            name: levelName
          }
        );

        dimension.hierarchies[hierarchyIndex].levels[levelIndex][
          'isDragDisabled'
        ] = isDragDisabled;

        return { ...dimension };
      } else {
        return dimension;
      }
    }

    return dimension;
  });
};

export const dragEnableLevelFromAxis = (
  dimensionsDataState,
  axesStates,
  axisName,
  isDragDisabled = false
) => {
  const dimensionsDataStateClone = cloneDeep(dimensionsDataState);
  const itemsClone = cloneDeep(axesStates[axisName].items);

  const [newState] = itemsClone.map(item => {
    const dimensionsData = dimensionsDataStateClone.map(dimension => {
      if (dimension.name === item.dimension) {
        const hierarchyIndex = findIndex(dimension.hierarchies, {
          uniqueName: item.name
        });

        if (hierarchyIndex !== -1) {
          const levels = Object.keys(item.levels);

          levels.forEach(level => {
            const levelIndex = findIndex(
              dimension.hierarchies[hierarchyIndex].levels,
              {
                hierarchyUniqueName: item.name,
                name: level
              }
            );

            dimension.hierarchies[hierarchyIndex].levels[levelIndex][
              'isDragDisabled'
            ] = isDragDisabled;
          });

          return { ...dimension };
        } else {
          return dimension;
        }
      }

      return dimension;
    });

    return dimensionsData;
  });

  return newState;
};

export const dragEnableLevelFromHierarchy = (
  dimensionsDataState,
  hierarchyItems,
  draggableSource,
  isDragDisabled = false
) => {
  const dimensionsDataStateClone = cloneDeep(dimensionsDataState);
  const itemClone = cloneDeep(hierarchyItems[draggableSource.index]);

  return dimensionsDataStateClone.map(dimension => {
    if (dimension.name === itemClone.dimension) {
      const hierarchyIndex = findIndex(dimension.hierarchies, {
        uniqueName: itemClone.name
      });

      if (hierarchyIndex !== -1) {
        const levels = Object.keys(itemClone.levels);

        levels.forEach(level => {
          const levelIndex = findIndex(
            dimension.hierarchies[hierarchyIndex].levels,
            {
              hierarchyUniqueName: itemClone.name,
              name: level
            }
          );

          dimension.hierarchies[hierarchyIndex].levels[levelIndex][
            'isDragDisabled'
          ] = isDragDisabled;
        });

        return { ...dimension };
      } else {
        return dimension;
      }
    }

    return dimension;
  });
};

export const copyLevel = (axesStates, levelData, droppableDestination) => {
  const axesStatesClone = cloneDeep(axesStates);
  const existingAxis = findAxisForHierarchy(
    axesStates,
    levelData.hierarchyUniqueName
  );
  const draggableSource = existingAxis
    ? { draggableId: existingAxis.droppableId, index: existingAxis.index }
    : null;
  const droppableDestinationClone = existingAxis
    ? existingAxis
    : droppableDestination;
  const axisName = droppableDestinationClone.droppableId;
  const destinationClone = cloneDeep(axesStatesClone[axisName].items);
  const hierarchy = {
    dimension: levelData.dimension,
    name: levelData.hierarchyUniqueName,
    caption: levelData.hierarchy,
    levels: {}
  };
  const hierarchyIndex = findIndex(destinationClone, {
    name: hierarchy.name
  });

  if (hierarchyIndex !== -1) {
    destinationClone[hierarchyIndex].levels[levelData.name] = {
      name: levelData.name
    };
  } else {
    hierarchy.levels[levelData.name] = { name: levelData.name };
    destinationClone.splice(droppableDestinationClone.index, 0, {
      ...hierarchy
    });
  }

  const axis = {
    ...axesStates[axisName],
    items: destinationClone
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  // If hierarchy already exists on an axis, move it to the hierarchy axis
  // and after that for the new destination axis
  return existingAxis &&
    draggableSource.draggableId !== droppableDestination.droppableId
    ? moveHierarchy(newState, draggableSource, droppableDestination)
    : newState;
};

export const reorderHierarchy = (
  axesStates,
  hierarchyItems,
  draggableSource,
  droppableDestination
) => {
  const axisName = droppableDestination.droppableId;
  const startIndex = draggableSource.index;
  const endIndex = droppableDestination.index;
  const hierarchyItemsClone = cloneDeep(hierarchyItems);
  const [removed] = hierarchyItemsClone.splice(startIndex, 1);

  hierarchyItemsClone.splice(endIndex, 0, removed);

  const axis = {
    ...axesStates[axisName],
    items: hierarchyItemsClone
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  return newState;
};

export const moveHierarchy = (
  axesStates,
  draggableSource,
  droppableDestination
) => {
  const axisNameSource = draggableSource.draggableId;
  const axisNameDestination = droppableDestination.droppableId;
  const startIndex = draggableSource.index;
  const endIndex = droppableDestination.index;
  const hierarchyItemsSourceClone = cloneDeep(axesStates[axisNameSource].items);
  const hierarchyItemsDestinationClone = cloneDeep(
    axesStates[axisNameDestination].items
  );
  const [hierarchyRemovedSource] = hierarchyItemsSourceClone.splice(
    startIndex,
    1
  );

  hierarchyItemsDestinationClone.splice(endIndex, 0, hierarchyRemovedSource);

  const axisSource = {
    ...axesStates[axisNameSource],
    items: hierarchyItemsSourceClone
  };

  const axisDestination = {
    ...axesStates[axisNameDestination],
    items: hierarchyItemsDestinationClone
  };

  const newState = {
    ...axesStates,
    [axisNameSource]: {
      ...axisSource
    },
    [axisNameDestination]: {
      ...axisDestination
    }
  };

  return newState;
};

export const removeHierarchy = (
  axesStates,
  hierarchyItems,
  draggableSource
) => {
  const axisName = draggableSource.draggableId;
  const oldIndex = draggableSource.index;
  const hierarchyItemsClone = cloneDeep(hierarchyItems);

  hierarchyItemsClone.splice(oldIndex, 1);

  const axis = {
    ...axesStates[axisName],
    items: hierarchyItemsClone
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  return newState;
};

export const removeLevel = (
  axesStates,
  hierarchyItems,
  levelIndex,
  draggableSource
) => {
  const axisName = draggableSource.draggableId;
  const hierarchyIndex = draggableSource.index;
  const hierarchyItemsClone = cloneDeep(hierarchyItems);
  const levelsClone = hierarchyItemsClone[hierarchyIndex].levels;
  const levelToRemove = Object.keys(levelsClone)[levelIndex];

  delete levelsClone[levelToRemove];

  const axis = {
    ...axesStates[axisName],
    items: hierarchyItemsClone
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  return newState;
};

export const setMembers = (
  axesStates,
  hierarchyItems,
  level,
  selections,
  draggableSource
) => {
  const axisName = draggableSource.draggableId;
  const hierarchyIndex = draggableSource.index;
  const hierarchyItemsClone = cloneDeep(hierarchyItems);
  const levelClone = hierarchyItemsClone[hierarchyIndex].levels[level];

  levelClone.selection = selections;

  const axis = {
    ...axesStates[axisName],
    items: hierarchyItemsClone
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  return newState;
};

export const swapAxes = axesStates => {
  const tmpAxisRows = cloneDeep(axesStates[ROWS]);

  const newState = {
    ...axesStates,
    [ROWS]: {
      ...axesStates[COLUMNS]
    },
    [COLUMNS]: {
      ...tmpAxisRows
    }
  };

  return newState;
};

export const clearDimensions = (axesStates, axisName) => {
  const axis = {
    ...axesStates[axisName],
    items: []
  };

  const newState = {
    ...axesStates,
    [axisName]: {
      ...axis
    }
  };

  return newState;
};
